package dao

import (
	"context"
	"errors"
	"github.com/ecodeclub/ekit/syncx/atomicx"
	"gorm.io/gorm"
	"webook/pkg/logger"
)

var errUnknowPattern = errors.New("未知双写模式")

// 双写方案思路1 直接修改dao
type DoubleWriteDAO struct {
	src     InteractiveDao
	dst     InteractiveDao
	pattern *atomicx.Value[string]
	l       logger.LoggerV1
}

func NewDoubleWriteDAO(src *gorm.DB, dst *gorm.DB, l logger.LoggerV1) *DoubleWriteDAO {
	return &DoubleWriteDAO{src: NewGORMInteractiveDao(src),
		dst:     NewGORMInteractiveDao(dst),
		l:       l,
		pattern: atomicx.NewValueOf(PatternSrcOnly),
	}
}

func (d *DoubleWriteDAO) UpdatePattern(pattern string) {
	d.pattern.Store(pattern)
}

func (d *DoubleWriteDAO) IncrReadCnt(ctx context.Context, biz string, bizId int64) error {
	pattern := d.pattern.Load()
	switch pattern {
	case PatternSrcOnly:
		return d.src.IncrReadCnt(ctx, biz, bizId)
	case PatternSrcFirst:
		err := d.src.IncrReadCnt(ctx, biz, bizId)
		if err != nil { //无法避免数据不一致
			return err
		}
		err = d.dst.IncrReadCnt(ctx, biz, bizId)
		if err != nil {
			//一般不要return err 正常来说，第一阶段 src成功了就认为业务上成功
			d.l.Error("双写写入dst失败 ", logger.Error(err), logger.String("biz", biz), logger.Int64("bizId", bizId))
		}
		return nil
	case PatternDstFirst:
		err := d.dst.IncrReadCnt(ctx, biz, bizId)
		if err == nil {
			er := d.src.IncrReadCnt(ctx, biz, bizId)
			if er != nil {
				//一般不要return err 正常来说，第一阶段 src成功了就认为业务上成功
				d.l.Error("双写写入src失败 ", logger.Error(err), logger.String("biz", biz), logger.Int64("bizId", bizId))
			}
		}
		return err
	case PatternDstOnly:
		return d.dst.IncrReadCnt(ctx, biz, bizId)
	default:
		return errUnknowPattern
	}

}

func (d *DoubleWriteDAO) BatchIncrReadCnt(ctx context.Context, bizs []string, bizIds []int64) error {
	//TODO implement me
	panic("implement me")
}

func (d *DoubleWriteDAO) IncrLikeInfo(ctx context.Context, biz string, bizId int64, uid int64) error {
	//TODO implement me
	panic("implement me")
}

func (d *DoubleWriteDAO) DeleteLikeInfo(ctx context.Context, biz string, bizId int64, uid int64) error {
	//TODO implement me
	panic("implement me")
}

func (d *DoubleWriteDAO) InsertCollectionBiz(ctx context.Context, cb UserCollectionBiz) error {
	//TODO implement me
	panic("implement me")
}

func (d *DoubleWriteDAO) GetLikedInfo(ctx context.Context, biz string, bizId int64, uid int64) (UserLikeBiz, error) {
	//TODO implement me
	panic("implement me")
}

func (d *DoubleWriteDAO) GetCollectedInfo(ctx context.Context, biz string, bizId int64, uid int64) (UserCollectionBiz, error) {
	//TODO implement me
	panic("implement me")
}

func (d *DoubleWriteDAO) Get(ctx context.Context, biz string, bizId int64) (Interactive, error) {
	pattern := d.pattern.Load()
	switch pattern {
	case PatternSrcFirst, PatternSrcOnly:
		return d.src.Get(ctx, biz, bizId)
	case PatternDstFirst, PatternDstOnly:
		return d.dst.Get(ctx, biz, bizId)
	default:
		return Interactive{}, errUnknowPattern
	}
}
func (d *DoubleWriteDAO) GetV1(ctx context.Context, biz string, bizId int64) (Interactive, error) {
	pattern := d.pattern.Load()
	switch pattern {
	case PatternSrcFirst, PatternSrcOnly:
		intr, err := d.src.Get(ctx, biz, bizId)
		if err == nil {
			go func() {
				intrDst, er := d.dst.Get(ctx, biz, bizId)
				if er == nil && intr != intrDst {
					//记录日志并通知修复程序 利用查询接口进行数据修复
					//不建议 耦合的修复 更倾向于全量修复和增量修复
				}
			}()
		}
		return intr, err
	case PatternDstFirst, PatternDstOnly:
		return d.dst.Get(ctx, biz, bizId)
	default:
		return Interactive{}, errUnknowPattern
	}
}
func (d *DoubleWriteDAO) GetByIds(ctx context.Context, biz string, ids []int64) ([]Interactive, error) {
	//TODO implement me
	panic("implement me")
}

const (
	PatternSrcOnly  = "src_only"
	PatternSrcFirst = "src_first"
	PatternDstFirst = "dst_first"
	PatternDstOnly  = "dst_only"
)
